package com.team.cloudsharing.service.folder;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.CopyObjectRequest;
import com.amazonaws.services.s3.model.DeleteObjectRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.team.cloudsharing.dao.actionlog.ActionLogDAO;
import com.team.cloudsharing.dao.actionlog.ActionLogDO;
import com.team.cloudsharing.dao.file.FileDAO;
import com.team.cloudsharing.dao.file.FileDO;
import com.team.cloudsharing.dao.folder.FolderDAO;
import com.team.cloudsharing.dao.folder.FolderDO;
import com.team.cloudsharing.dao.sharefolder.ShareFolderDAO;
import com.team.cloudsharing.dao.sharefolder.ShareFolderDO;
import com.team.cloudsharing.dao.user.UserDAO;
import com.team.cloudsharing.dao.user.UserDO;
import com.team.cloudsharing.dto.Folder;
import com.team.cloudsharing.dto.FolderInformation;
import com.team.cloudsharing.dto.PathDTO;
import com.team.cloudsharing.service.file.FileService;
import com.team.cloudsharing.service.sharefolder.ShareFolderService;
import com.team.cloudsharing.service.user.UserService;
import com.team.cloudsharing.ultis.Constant;
import com.team.cloudsharing.ultis.IDGenerator;
import com.team.cloudsharing.ultis.S3Util;

@Service("folderService")
public class FolderServiceImpl implements FolderService {

	final static Logger logger = Logger.getLogger(FolderServiceImpl.class);

	@Autowired
	FolderDAO folderDAO;
	
	@Autowired
	FileService fileService;
	
	@Autowired
	ShareFolderService shareFolderService;
	
	@Autowired
	FileDAO fileDAO;
	
	@Autowired
	ActionLogDAO actionLogDAO;
	
	@Autowired
	UserDAO userDAO;
	
	@Autowired
	UserService userService;
	
	@Autowired
	ShareFolderDAO shareFolderDAO;

	@Value("${AMAZON_ACCESS_KEY}")
	String amazonAccessKey;

	@Value("${AMAZON_SECRET_KEY}")
	String amazonSecretKey;

	@Value("${AMAZON_BUCKET}")
	String amazonBucket;

	@Override
	public String createFolder(Folder folder) {
		System.out.println(folder.getUserEmail());
		System.out.println(folder.getFolderPath());
		/*
		 * Luu y:
		 * Khi tao folder neu folder nam tai thu muc ROOT thi truyen folderPath = ""
		 * Neu folder nam trong folder 'TTT' thi folderPath = TTT
		 * Neu folder nam trong folder 'KKK' va 'KKK' nam trong 'TTT' thi folderPath = TTT/KKK
		 */
		UserDO userDO = userDAO.findUserByUserEmail(folder.getUserEmail());

		//System.out.println(folder.getUserEmail() + " - " + folder.getFolderPath() + " - " + folder.getFolderName());
		//System.out.println(userDO.getUserEmail() + " - " + folderDOROOT.getFolderId());

		FolderDO folderDOCheck = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(folder.getFolderName(), folder.getFolderPath(), userDO);
		
		Folder checkFolderShare = checkFolderInShareFoldersOfUser(folder);
		
		if (checkFolderShare == null){
			return Constant.CREATE_FOLDER_FAILURE;
		}
		else if(checkFolderShare != null && folderDOCheck == null){
			folder = checkFolderShare;
			userDO = userDAO.findUserByUserEmail(folder.getUserEmail());
			
			//If folder in share folder is exist => return null
			FolderDO folderDOParent = getFolderDOParentFromFolderPath(folder.getFolderPath(), userDO);
			
			FolderDO folderDOInsert = new FolderDO();			
			folderDOInsert.setFolderName(folder.getFolderName());
			folderDOInsert.setFolderUrl(getRandomFolderUrl());
			folderDOInsert.setFolderIdParent(folderDOParent.getFolderId());
			folderDOInsert.setFolderPath(folder.getFolderPath());
			folderDOInsert.setFolderPermission(folder.getFolderPermission());
			folderDOInsert.setUserDO(userDO);
			folderDAO.insertFolder(folderDOInsert);
			//Call S3 create folder
			FolderDO folderDOROOT = folderDAO.findROOTFolderOfUser(userDO);
			if (folder.getFolderPath().isEmpty()){
				folder.setFolderPath(folderDOROOT.getFolderName() + Constant.SUFFIX);
			}
			else{
				folder.setFolderPath(folderDOROOT.getFolderName() + Constant.SUFFIX + folder.getFolderPath() + Constant.SUFFIX);
			}
			this.createS3Folder(folder);
			List<ActionLogDO> actionLogDOs = actionLogDAO.setAllStatusByUserDO(userDO);
			if (actionLogDOs != null) {
			 for (ActionLogDO actionLogDO : actionLogDOs) {
					actionLogDO.setStatus(true);
					actionLogDAO.insertActionLog(actionLogDO);
				}
			}
			if (folder.getMacAddress()!=null && !folder.getMacAddress().isEmpty()){
				actionLogDAO.setStatusByMacAddress(folder.getMacAddress());
			}
			shareFolderService.sendNotificationSynchoronize(folder.getUserEmail());
			return Constant.CREATE_FOLDER_SUCCESS;
		}
		else{
			if(restoreFolderInTrashWhenCreate(folderDOCheck)){
				List<ActionLogDO> actionLogDOs = actionLogDAO.setAllStatusByUserDO(userDO);
				if (actionLogDOs != null) {
				 for (ActionLogDO actionLogDO : actionLogDOs) {
						actionLogDO.setStatus(true);
						actionLogDAO.insertActionLog(actionLogDO);
					}
				}
				if (folder.getMacAddress()!=null && !folder.getMacAddress().isEmpty()){
					actionLogDAO.setStatusByMacAddress(folder.getMacAddress());
				}
				shareFolderService.sendNotificationSynchoronize(folder.getUserEmail());
				return Constant.CREATE_FOLDER_SUCCESS;
			}
			else{
				return Constant.CREATE_FOLDER_FAILURE;
			}
		}
	}
	
	private Folder checkFolderInShareFoldersOfUser(Folder folder){
		//If folderDOCheck is not exist in user's folder => find in Share folder
		FolderDO realFolderDOShare = getRealFolderDOFromShareFolder(folder.getFolderPath(), folder.getUserEmail());
		if (realFolderDOShare != null){
			//Get real folder Path and folder name and userEmail of folder
			String realPathToFolderDOShare = null;
			if (realFolderDOShare.getFolderPath().isEmpty()){
				realPathToFolderDOShare = realFolderDOShare.getFolderName();
			}
			else{
				realPathToFolderDOShare = realFolderDOShare.getFolderPath() + Constant.SUFFIX + realFolderDOShare.getFolderName();
			}
			FolderDO folderDOCheck = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(folder.getFolderName(), realPathToFolderDOShare, realFolderDOShare.getUserDO());
			
			if (folderDOCheck != null){
				return null;
			}
			folder.setFolderPath(realPathToFolderDOShare);
			folder.setUserEmail(realFolderDOShare.getUserDO().getUserEmail());
		}
		return folder;
	}
	
	private FolderDO getFolderDOParentFromFolderPath(String folderPath, UserDO userDO){
		FolderDO folderDO = null;
		if(folderPath.isEmpty()){
			folderDO = folderDAO.findROOTFolderOfUser(userDO);
		}
		else{
			String[] folderNameAndFolderPath = splitFolderNameParentAndFolderPathParentInFolderPath(folderPath);
			folderDO = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(folderNameAndFolderPath[0], folderNameAndFolderPath[1], userDO);
		}
		return folderDO;
	}
	
	private String[] splitFolderNameParentAndFolderPathParentInFolderPath(String folderPath){
		String[] folderNameAndFolderPath = new String[2];
		String[] folderPathSplit = folderPath.split(Constant.SUFFIX);
		if (folderPathSplit.length == 1){
			folderNameAndFolderPath[0] = folderPathSplit[0];
			folderNameAndFolderPath[1] = Constant.EMPTY_PATH;
		}
		else {
			folderNameAndFolderPath[0] = folderPathSplit[folderPathSplit.length - 1];
			folderNameAndFolderPath[1] = Constant.EMPTY_PATH;
			for(int i=0; i < folderPathSplit.length - 1; i++)
				folderNameAndFolderPath[1] += folderPathSplit[i] + Constant.SUFFIX;
			folderNameAndFolderPath[1] = removeCharactersSUFFIXInTheEnd(folderNameAndFolderPath[1]);
		}
		return folderNameAndFolderPath;
	}
	
	private String removeCharactersSUFFIXInTheEnd(String stringInput){
		if (! stringInput.isEmpty() && stringInput.charAt(stringInput.length() - 1) == '/'){
			stringInput = stringInput.substring(0, stringInput.length() - 1);
		}
		return stringInput;
	}
	@Override
	public FolderDO getRealFolderDOFromShareFolder(String folderPath, String userEmail){
		FolderDO folderDOGetFromShareFolder = null;
		String[] pathSplit = folderPath.split(Constant.SUFFIX);
		String shareFolderName = pathSplit[0];
		UserDO userDO = userDAO.findUserByUserEmail(userEmail);
		ShareFolderDO shareFolderDO = shareFolderDAO.findShareFolderByFolderNameAndUserId(shareFolderName, userDO); 
		if (shareFolderDO != null){
			FolderDO folderDOShared = folderDAO.findFolderById(shareFolderDO.getFolderDO().getFolderId());
			if (! userEmail.equals(folderDOShared.getUserDO().getUserEmail())){
				String realPath = ""; 
				if (folderDOShared.getFolderPath().isEmpty()){
					realPath = folderDOShared.getFolderName();
				}
				else{
					realPath = folderDOShared.getFolderPath() + Constant.SUFFIX + folderDOShared.getFolderName();
				}
				for (int i = 1; i < pathSplit.length; i++){
					realPath = realPath + Constant.SUFFIX + pathSplit[i];
				}
				String[] folderNameAndFolderPath = splitFolderNameParentAndFolderPathParentInFolderPath(realPath);
				folderDOGetFromShareFolder = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(folderNameAndFolderPath[0], folderNameAndFolderPath[1], folderDOShared.getUserDO());
			}
		}
		return folderDOGetFromShareFolder;
	}
	
	private boolean restoreFolderInTrashWhenCreate(FolderDO folderDO){
		if (folderDO.isFolderInTrash()){
			folderDO.setFolderInTrash(false);
			folderDAO.updateFolder(folderDO);
			return true;
		}
		return false;
	}
	
	private String getRandomFolderUrl(){
		String folderUrlRandom = null;
		do{
			folderUrlRandom = IDGenerator.randomString(10);
		}
		while(folderDAO.findFolderByFolderUrl(folderUrlRandom) != null);
		return folderUrlRandom;
	}
	
	@Override
	public String createFolderROOT(Folder folder){
		/* This function is called when activate account successfully */
		String statusCreate = Constant.CREATE_FOLDER_SUCCESS;
		UserDO userDO = userDAO.findUserByUserEmail(folder.getUserEmail());
		FolderDO folderDOCheck = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(folder.getFolderName(), folder.getFolderPath(), userDO);
		if (folderDOCheck == null){
			FolderDO folderDOInsert = new FolderDO(folder.getFolderName(), getRandomFolderUrl(), Constant.FOLDER_ID_PARENT_OF_ROOT, false, false, Constant.DOCUMENT_PRIVATE, userDO);
			folderDAO.insertFolder(folderDOInsert);
			//Call S3 create folder
			folder.setFolderPath("");
			this.createS3Folder(folder);
		}
		else{
			statusCreate = Constant.FOLDER_ROOT_EXIST;
		}
		return statusCreate;
	}

	@Override
	public String moveFolderToTrash(Folder folder) {
		String statusMoveToTrash = Constant.MOVE_FOLDER_TO_TRASH_FAILURE;
		UserDO userDO = userDAO.findUserByUserEmail(folder.getUserEmail());
		FolderDO folderDO = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(folder.getFolderName(), folder.getFolderPath(), userDO);
		
		System.out.println(folder.getFolderPath() + " - " + folder.getFolderName() + " - " + folderDO);
		if (folderDO != null){
			String folderChildPath = "";
			if (folderDO.getFolderPath().isEmpty()){
				folderChildPath = folderDO.getFolderName();
			}
			else{
				folderChildPath = folderDO.getFolderPath() + Constant.SUFFIX + folderDO.getFolderName();
			}
			List<FolderDO> listFolderDOChild = folderDAO.findAllFolderByFolderPathAndUserId(folderChildPath, userDO, "");
			if (listFolderDOChild.size() > 0){
				moveChildFolderToTrash(listFolderDOChild, folder.getUserEmail());
			}
			
			logger.info("Move to trash folder: " + folderDO.getFolderPath() + Constant.SUFFIX + folderDO.getFolderName());

			folderDO.setFolderInTrash(true);
			Long folderIdUpdated = folderDAO.updateFolder(folderDO);
			
			if (folderIdUpdated > 0) {
				statusMoveToTrash = Constant.MOVE_FOLDER_TO_TRASH_SUCCESS;
				shareFolderService.sendNotificationSynchoronize(folder.getUserEmail());
				List<ActionLogDO> actionLogDOs = actionLogDAO.setAllStatusByUserDO(userDO);
				if (actionLogDOs != null) {
				 for (ActionLogDO actionLogDO : actionLogDOs) {
						actionLogDO.setStatus(true);
						actionLogDAO.insertActionLog(actionLogDO);
					}
				}
				if (folder.getMacAddress()!=null && !folder.getMacAddress().isEmpty()){
					actionLogDAO.setStatusByMacAddress(folder.getMacAddress());
				}
			}
			
			//Move all file In folder to trash
			List<FileDO> listFileChild = fileDAO.findAllFileInFolder(folderDO, "");
			moveAllFileInFolderToTrash(listFileChild);
		}
		else{
			ShareFolderDO shareFolderDO = shareFolderDAO.findShareFolderByFolderNameAndUserId(folder.getFolderName(), userDO);
			if (shareFolderDO != null){
				shareFolderDO.setShareFolderInTrash(true);
				long shareFolderId = shareFolderDAO.updateShareFolder(shareFolderDO);
				if (shareFolderId > 0){
					statusMoveToTrash = Constant.MOVE_FOLDER_TO_TRASH_SUCCESS;
					shareFolderService.sendNotificationSynchoronize(folder.getUserEmail());
					List<ActionLogDO> actionLogDOs = actionLogDAO.setAllStatusByUserDO(userDO);
					if (actionLogDOs != null) {
					 for (ActionLogDO actionLogDO : actionLogDOs) {
							actionLogDO.setStatus(true);
							actionLogDAO.insertActionLog(actionLogDO);
						}
					}
					if (folder.getMacAddress()!=null && !folder.getMacAddress().isEmpty()){
						actionLogDAO.setStatusByMacAddress(folder.getMacAddress());
					}
				}
			}
		}
		return statusMoveToTrash;
	}
	
	private void moveChildFolderToTrash(List<FolderDO> listFolderDOChild, String userEmail){
		UserDO userDO = userDAO.findUserByUserEmail(userEmail);
		for(FolderDO folderDO : listFolderDOChild){
			String folderChildPath = "";
			if (folderDO.getFolderPath() != null){
				folderChildPath = folderDO.getFolderPath() + Constant.SUFFIX + folderDO.getFolderName();
			}
			else{
				folderChildPath = folderDO.getFolderName();
			}
			List<FolderDO> listFolderDOChildChilds = folderDAO.findAllFolderByFolderPathAndUserId(folderChildPath, userDO, "");
			if (listFolderDOChildChilds.size() > 0){
				moveChildFolderToTrash(listFolderDOChildChilds, userEmail); 
			}
			
			logger.info("Move to trash folder: " + folderDO.getFolderPath() + Constant.SUFFIX + folderDO.getFolderName());
			folderDO.setFolderInTrash(true);
			folderDAO.updateFolder(folderDO);
			
			//Move all file In folder to trash
			List<FileDO> listFileChild = fileDAO.findAllFileInFolder(folderDO, "");
			moveAllFileInFolderToTrash(listFileChild);
		}
	}
	
	private void moveAllFileInFolderToTrash(List<FileDO> listFileChild){
		for (FileDO fileDO : listFileChild) {
			logger.info("Move to trash File: " + fileDO.getFilePath() + Constant.SUFFIX + fileDO.getFileName());
			fileDO.setFileInTrash(true);
			fileDAO.updateFile(fileDO);
		}
	}
	
	@Override
	public String restoreFolderFromTrash(Folder folder) {
		String statusRestoreFromTrash = Constant.RESTORE_FOLDER_FROM_TRASH_FAILURE;
		UserDO userDO = userDAO.findUserByUserEmail(folder.getUserEmail());
		FolderDO folderDO = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(folder.getFolderName(), folder.getFolderPath(), userDO);
		System.out.println(folder.getFolderPath() + " - " + folder.getFolderName() + " - " + folderDO);
		if (folderDO != null){
			FolderDO folderDOParent = folderDAO.findFolderById(folderDO.getFolderIdParent());
			if (! folderDOParent.isFolderInTrash()){
				folderDO.setFolderInTrash(false);
				Long folderIdUpdated = folderDAO.updateFolder(folderDO);
				folderDO = folderDAO.findFolderById(folderDO.getFolderId());
				restoreAllFileInFolderFromTrash(folderDO);
				if (folderIdUpdated > 0) {
					statusRestoreFromTrash = Constant.RESTORE_FOLDER_FROM_TRASH_SUCCESS;
					shareFolderService.sendNotificationSynchoronize(folder.getUserEmail());
					List<ActionLogDO> actionLogDOs = actionLogDAO.setAllStatusByUserDO(userDO);
					if (actionLogDOs != null) {
					 for (ActionLogDO actionLogDO : actionLogDOs) {
							actionLogDO.setStatus(true);
							actionLogDAO.insertActionLog(actionLogDO);
						}
					}
					if (folder.getMacAddress()!=null && !folder.getMacAddress().isEmpty()){
						actionLogDAO.setStatusByMacAddress(folder.getMacAddress());
					}
				}
			}
			else{
				statusRestoreFromTrash = Constant.RESTORE_FOLDER_FAILURE_BECAUSE_FOLDER_PARENT;
			}
			//Restore all folder from folder
			String folderChildPath = "";
			if (folderDO.getFolderPath().isEmpty()){
				folderChildPath = folderDO.getFolderName();
			}
			else{
				folderChildPath = folderDO.getFolderPath() + Constant.SUFFIX + folderDO.getFolderName();
			}
			List<FolderDO> listFolderDOChild = folderDAO.findAllFolderByFolderPathAndUserId(folderChildPath, userDO, "true");
			if (listFolderDOChild.size() > 0){
				restoreChildFolderFromTrash(listFolderDOChild, folder.getUserEmail());
			}
		}
		else{
			ShareFolderDO shareFolderDO = shareFolderDAO.findShareFolderByFolderNameAndUserId(folder.getFolderName(), userDO);
			if (shareFolderDO != null){
				shareFolderDO.setShareFolderInTrash(false);
				long shareFolderId = shareFolderDAO.updateShareFolder(shareFolderDO);
				if (shareFolderId > 0){
					statusRestoreFromTrash = Constant.RESTORE_FOLDER_FROM_TRASH_SUCCESS;
					shareFolderService.sendNotificationSynchoronize(folder.getUserEmail());
					List<ActionLogDO> actionLogDOs = actionLogDAO.setAllStatusByUserDO(userDO);
					if (actionLogDOs != null) {
					 for (ActionLogDO actionLogDO : actionLogDOs) {
							actionLogDO.setStatus(true);
							actionLogDAO.insertActionLog(actionLogDO);
						}
					}
					if (folder.getMacAddress()!=null && !folder.getMacAddress().isEmpty()){
						actionLogDAO.setStatusByMacAddress(folder.getMacAddress());
					}
				}
			}
		}
		return statusRestoreFromTrash;
	}
	
	private void restoreChildFolderFromTrash(List<FolderDO> listFolderDOChild, String userEmail){
		UserDO userDO = userDAO.findUserByUserEmail(userEmail);
		String folderChildPath = "";
		for(FolderDO folderDO : listFolderDOChild){
			if (folderDO.getFolderPath() != null){
				folderChildPath = folderDO.getFolderPath() + Constant.SUFFIX + folderDO.getFolderName();
			}
			else{
				folderChildPath = folderDO.getFolderName();
			}
			List<FolderDO> listFolderDOChildChilds = folderDAO.findAllFolderByFolderPathAndUserId(folderChildPath, userDO, "");
			if (listFolderDOChildChilds.size() > 0){
				restoreChildFolderFromTrash(listFolderDOChildChilds, userEmail); 
			}
			
			folderDO.setFolderInTrash(false);
			folderDAO.updateFolder(folderDO);
			
			//Restore all file In folder to trash
			restoreAllFileInFolderFromTrash(folderDO);
		}
	}
	
	private void restoreAllFileInFolderFromTrash(FolderDO folderDO){
		List<FileDO> listFileDO = fileDAO.findAllFileInFolder(folderDO, "true");
		for (FileDO fileDO : listFileDO) {
			fileDO.setFileInTrash(false);
			fileDAO.updateFile(fileDO);
		}
	}
	
	@Override
	public String restoreFolderWithFolderParent(Folder folder, boolean isRestoreAll) {
		String statusRestoreFromTrash = Constant.RESTORE_FOLDER_FROM_TRASH_FAILURE;
		UserDO userDO = userDAO.findUserByUserEmail(folder.getUserEmail());
		FolderDO folderDO = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(folder.getFolderName(), folder.getFolderPath(), userDO);
		if (folderDO != null){
			FolderDO folderDOParent = folderDAO.findFolderById(folderDO.getFolderIdParent());
			if (folderDOParent.getFolderIdParent() > 0){
				Folder folderParent = new Folder();
				folderParent.setUserEmail(folder.getUserEmail());
				folderParent.setFolderName(folderDOParent.getFolderName());
				folderParent.setFolderPath(folderDOParent.getFolderPath());
				statusRestoreFromTrash = restoreFolderWithFolderParent(folderParent, false);
			}
			else{
				statusRestoreFromTrash = Constant.RESTORE_FOLDER_FROM_TRASH_SUCCESS;
				shareFolderService.sendNotificationSynchoronize(folder.getUserEmail());
				List<ActionLogDO> actionLogDOs = actionLogDAO.setAllStatusByUserDO(userDO);
				if (actionLogDOs != null) {
				 for (ActionLogDO actionLogDO : actionLogDOs) {
						actionLogDO.setStatus(true);
						actionLogDAO.insertActionLog(actionLogDO);
					}
				}
				if (folder.getMacAddress()!=null && !folder.getMacAddress().isEmpty()){
					actionLogDAO.setStatusByMacAddress(folder.getMacAddress());
				}
			}
			folderDO.setFolderInTrash(false);
			folderDAO.updateFolder(folderDO);
			folderDO = folderDAO.findFolderById(folderDO.getFolderId());
			if (isRestoreAll == true)
				restoreAllFileInFolderFromTrash(folderDO);
		}
		return statusRestoreFromTrash;
	}
	
	@Override
	public String deleteFolder(Folder folder) {
		String statusDeleteFolder = Constant.DELETE_FOLDER_FAILURE;
		UserDO userDO = userDAO.findUserByUserEmail(folder.getUserEmail());
		FolderDO folderDOCheck = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(folder.getFolderName(), folder.getFolderPath(), userDO);
		System.out.println(folderDOCheck + " --- " + folder.getFolderName() + folder.getFolderPath()+folder.getUserEmail());
		long capacityWillDecrease = 0;
		if (folderDOCheck != null){
			String folderChildPath = "";
			if (folderDOCheck.getFolderPath().isEmpty()){
				folderChildPath = folderDOCheck.getFolderName();
			}
			else{
				folderChildPath = folderChildPath + folderDOCheck.getFolderPath() + Constant.SUFFIX + folderDOCheck.getFolderName();
			}
			System.out.println(folderChildPath + "------------");
			List<FolderDO> listFolderDOChilds = folderDAO.findAllFolderByFolderPathAndUserId(folderChildPath, userDO, "");
			if (listFolderDOChilds.size() > 0){
				deleteChildFolder(listFolderDOChilds, userDO.getUserEmail());
			}
			List<FileDO> listFileDO = fileDAO.findAllFileInFolder(folderDOCheck, "");
			for (FileDO fileDO : listFileDO) {
				capacityWillDecrease += fileDO.getFileCapacity();
			}
			boolean status = folderDAO.deleteFolder(folderDOCheck.getFolderId());
			
			//Delete folder on Amazon S3
			if (folder.getFolderPath().isEmpty()){
				folder.setFolderPath(userDO.getUserEmail() + Constant.SUFFIX);
			}
			else {
				folder.setFolderPath(userDO.getUserEmail() + Constant.SUFFIX + folder.getFolderPath() + Constant.SUFFIX);
			}
			this.deleteS3Folder(folder);
			if (status) {
				statusDeleteFolder = Constant.DELETE_FOLDER_SUCCESS;
				shareFolderService.sendNotificationSynchoronize(folder.getUserEmail());
				List<ActionLogDO> actionLogDOs = actionLogDAO.setAllStatusByUserDO(userDO);
				if (actionLogDOs != null) {
				 for (ActionLogDO actionLogDO : actionLogDOs) {
						actionLogDO.setStatus(true);
						actionLogDAO.insertActionLog(actionLogDO);
					}
				}
				if (folder.getMacAddress()!=null && !folder.getMacAddress().isEmpty()){
					actionLogDAO.setStatusByMacAddress(folder.getMacAddress());
				}
			}
			
		}
		else{
			ShareFolderDO shareFolderDO = shareFolderDAO.findShareFolderByFolderNameAndUserId(folder.getFolderName(), userDO);
			if (shareFolderDO != null){
				if (shareFolderDAO.deleteShareFolder(shareFolderDO.getShareFolderId())){
					statusDeleteFolder = Constant.DELETE_FOLDER_SUCCESS;
					shareFolderService.sendNotificationSynchoronize(folder.getUserEmail());
					List<ActionLogDO> actionLogDOs = actionLogDAO.setAllStatusByUserDO(userDO);
					if (actionLogDOs != null) {
					 for (ActionLogDO actionLogDO : actionLogDOs) {
							actionLogDO.setStatus(true);
							actionLogDAO.insertActionLog(actionLogDO);
						}
					}
					if (folder.getMacAddress()!=null && !folder.getMacAddress().isEmpty()){
						actionLogDAO.setStatusByMacAddress(folder.getMacAddress());
					}
				}
			}
		}
		userService.decreaseUsedCapacity(folderDOCheck.getUserDO().getUserEmail(), capacityWillDecrease);
		return statusDeleteFolder;
	}
	
	private void deleteChildFolder(List<FolderDO> listFolderDOChilds, String userEmail){
		UserDO userDO = userDAO.findUserByUserEmail(userEmail);
		long capacityWillDecrease = 0;
		for(FolderDO folderDO : listFolderDOChilds){
			String folderChildPath = "";
			if (folderDO.getFolderPath().isEmpty()){
				folderChildPath = folderDO.getFolderName();
			}
			else{
				folderChildPath = folderDO.getFolderPath() + Constant.SUFFIX + folderDO.getFolderName();
			}
			List<FolderDO> listFolderDOChildChilds = folderDAO.findAllFolderByFolderPathAndUserId(folderChildPath, userDO, "");
			if (listFolderDOChildChilds.size() > 0){
				deleteChildFolder(listFolderDOChildChilds, userEmail); 
			}
			logger.info("Deleted folder: " + folderDO.getFolderPath() + Constant.SUFFIX + folderDO.getFolderName());
			List<FileDO> listFileDO = fileDAO.findAllFileInFolder(folderDO, "");
			for (FileDO fileDO : listFileDO) {
				capacityWillDecrease += fileDO.getFileCapacity();
				
			}
			folderDAO.deleteFolder(folderDO.getFolderId());
		}
		if (listFolderDOChilds.size() > 0){
			userService.decreaseUsedCapacity(listFolderDOChilds.get(0).getUserDO().getUserEmail(), capacityWillDecrease);
		}
	}

	@Override
	public boolean isFolderExist(long folderIdParent,
			String nameFolderCheckExist) {
		List<String> folderNamesHasFoderIdParent = folderDAO
				.getFolderNamesHasFoderIdParent(folderIdParent);
		if (folderNamesHasFoderIdParent == null) {
			return false;
		}
		for (String folderName : folderNamesHasFoderIdParent) {
			if (nameFolderCheckExist.equals(folderName)) {
				return true;
			}
		}
		return false;
	}

	/*
	 * Private function
	 */
	private void createS3Folder(Folder folder) {
		AWSCredentials credentials = new BasicAWSCredentials(amazonAccessKey,
				amazonSecretKey);
		AmazonS3 s3client = new AmazonS3Client(credentials);
		ObjectMetadata metadata = new ObjectMetadata();
		metadata.setContentLength(0);
		// create empty content
		InputStream emptyContent = new ByteArrayInputStream(new byte[0]);
		// create a PutObjectRequest passing the folder name suffixed by /
		PutObjectRequest putObjectRequest = new PutObjectRequest(amazonBucket, folder.getFolderPath() + folder.getFolderName() + Constant.SUFFIX, emptyContent, metadata);
		// send request to S3 to create folder
		s3client.putObject(putObjectRequest);
		System.out.println("Create Folder S3 Success");
	}
	
	private void deleteS3Folder(Folder folder) {
		AWSCredentials credentials = new BasicAWSCredentials(amazonAccessKey, amazonSecretKey);
		AmazonS3 s3client = new AmazonS3Client(credentials);
		for (S3ObjectSummary file : s3client.listObjects(amazonBucket, folder.getFolderPath() + folder.getFolderName() + Constant.SUFFIX).getObjectSummaries()){
			s3client.deleteObject(amazonBucket, file.getKey());
		}
	}
	
	@Override
	public String renameFolder(PathDTO pathDTO) {
		UserDO userDO = userDAO.findUserByUserEmail(pathDTO.getUserEmail());
		FolderDO folderDO = folderDAO.findFolderById(pathDTO.getDocumentId());
		
		ShareFolderDO shareFolderDO = shareFolderDAO.findShareFolderByFolderIdAndUserId(folderDO, userDO);
		if (shareFolderDO == null) {
			String newFilePath;
			String newFolderPath;
			FileDO fileDOForCheckPath;
			FolderDO folderDOForCheckPath;
			FolderDO folderParentDO = folderDAO.findFolderById(folderDO.getFolderIdParent());
			if (!checkFolderIsExist(pathDTO.getNewDocumentName(), folderParentDO)) {
				String oldFullPath = S3Util.convertDocumentPathAndNameToFullPathForS3(pathDTO.getUserEmail(), pathDTO.getOldDocumentPath(), pathDTO.getOldDocumentName(), pathDTO.getDocumentType());
				String newFullPath = S3Util.convertDocumentPathAndNameToFullPathForS3(pathDTO.getUserEmail(), pathDTO.getNewDocumentPath(), pathDTO.getNewDocumentName(), pathDTO.getDocumentType());
				
				String oldDocumentPath;
				String newDocumentPath;
				
				if (pathDTO.getOldDocumentPath().isEmpty()) {
					oldDocumentPath = pathDTO.getOldDocumentName();
					newDocumentPath = pathDTO.getNewDocumentName();
				} else {
					oldDocumentPath = pathDTO.getOldDocumentPath() + Constant.SUFFIX + pathDTO.getOldDocumentName();
					newDocumentPath = pathDTO.getNewDocumentPath() + Constant.SUFFIX + pathDTO.getNewDocumentName();
				}
				
				if (moveFolderS3(oldFullPath, newFullPath) == Constant.MOVE_FOLDER_SUCCESS) {
					List<FolderDO> folderDOs = folderDAO.findAllFolderByUserId(userDO);
					for (int i = 0; i < folderDOs.size(); i++) {
						List<FileDO> fileDOs = new ArrayList<FileDO>(folderDOs.get(i).getFileDO());
						for (int j = 0; j < fileDOs.size(); j++) {
							fileDOForCheckPath = fileDOs.get(j);
							if (StringUtils.startsWith(fileDOForCheckPath.getFilePath(), oldDocumentPath)) {
								newFilePath = StringUtils.replaceOnce(fileDOForCheckPath.getFilePath(), oldDocumentPath, newDocumentPath);
								fileDOForCheckPath.setFilePath(newFilePath);
								fileDAO.updateFile(fileDOForCheckPath);
							}
						}
						folderDOForCheckPath = folderDOs.get(i);
						if (StringUtils.startsWith(folderDOForCheckPath.getFolderPath(), oldDocumentPath)) {
							newFolderPath = StringUtils.replaceOnce(folderDOForCheckPath.getFolderPath(), oldDocumentPath, newDocumentPath);
							folderDOForCheckPath.setFolderPath(newFolderPath);
							folderDAO.updateFolder(folderDOForCheckPath);
						}
					}
					
					FolderDO oldFolderDO = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(pathDTO.getOldDocumentName(), pathDTO.getOldDocumentPath(), userDO);
					oldFolderDO.setFolderName(pathDTO.getNewDocumentName());
					folderDAO.updateFolder(oldFolderDO);
					List<ActionLogDO> actionLogDOs = actionLogDAO.setAllStatusByUserDO(userDO);
					if (actionLogDOs != null) {
					 for (ActionLogDO actionLogDO : actionLogDOs) {
							actionLogDO.setStatus(true);
							actionLogDAO.insertActionLog(actionLogDO);
						}
					}
					if (pathDTO.getMacAddress()!=null && !pathDTO.getMacAddress().isEmpty()){
						actionLogDAO.setStatusByMacAddress(pathDTO.getMacAddress());
					}
					shareFolderService.sendNotificationSynchoronize(pathDTO.getUserEmail());
					return Constant.RENAME_FOLDER_SUCCESS;
				}
			}
			return Constant.RENAME_FOLDER_FAILURE;
		} else {
			shareFolderDO.setUserFolderName(pathDTO.getNewDocumentName());
			shareFolderDAO.updateShareFolder(shareFolderDO);
			List<ActionLogDO> actionLogDOs = actionLogDAO.setAllStatusByUserDO(userDO);
			if (actionLogDOs != null) {
			 for (ActionLogDO actionLogDO : actionLogDOs) {
					actionLogDO.setStatus(true);
					actionLogDAO.insertActionLog(actionLogDO);
				}
			}
			if (pathDTO.getMacAddress()!=null && !pathDTO.getMacAddress().isEmpty()){
				actionLogDAO.setStatusByMacAddress(pathDTO.getMacAddress());
			}
			shareFolderService.sendNotificationSynchoronize(shareFolderDO.getUserDO().getUserEmail());
			return Constant.RENAME_FOLDER_SUCCESS;
		}
	}
	
	@Override
	public String moveFolderToAnotherFolder(PathDTO pathDTO) {
		
		String newFolderPath;
		String newFolderName;
		String newFolderNameIfExist = "";
		String newFullPath;
		if (pathDTO.getNewDocumentPath().isEmpty()) {
			newFolderPath = "";
			newFolderName = pathDTO.getUserEmail();
		} else {
			if (!pathDTO.getNewDocumentPath().contains(Constant.SUFFIX)) {
				newFolderPath = "";
				newFolderName = pathDTO.getNewDocumentPath();
			} else {
				newFolderPath = StringUtils.substringBeforeLast(pathDTO.getNewDocumentPath(), Constant.SUFFIX);
				newFolderName = StringUtils.substringAfterLast(pathDTO.getNewDocumentPath(), Constant.SUFFIX);
			}
		}
		UserDO userDO = userDAO.findUserByUserEmail(pathDTO.getUserEmail());
		FolderDO newFolderDO = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(newFolderName, newFolderPath, userDO);
		FolderDO oldFolderDO = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(pathDTO.getOldDocumentName(), pathDTO.getOldDocumentPath(), userDO);
		String newFilePath;
		String newFolderPathForUpdate;
		FileDO fileDOForCheckPath;
		FolderDO folderDOForCheckPath;
		String oldFullPath = S3Util.convertDocumentPathAndNameToFullPathForS3(pathDTO.getUserEmail(), pathDTO.getOldDocumentPath(), pathDTO.getOldDocumentName(), pathDTO.getDocumentType());
		
		if (!checkFolderIsExist(pathDTO.getNewDocumentName(), newFolderDO)) {
			newFullPath = S3Util.convertDocumentPathAndNameToFullPathForS3(pathDTO.getUserEmail(), pathDTO.getNewDocumentPath(), pathDTO.getNewDocumentName(), pathDTO.getDocumentType());
		} else {
			newFolderNameIfExist = makeNewFolderNameIfExist(pathDTO, newFolderDO);
			pathDTO.setNewDocumentName(newFolderNameIfExist);
			oldFolderDO.setFolderName(newFolderNameIfExist);
			newFullPath = S3Util.convertDocumentPathAndNameToFullPathForS3(pathDTO.getUserEmail(), pathDTO.getNewDocumentPath(), newFolderNameIfExist, pathDTO.getDocumentType()); 
		}
			
		if (moveFolderS3(oldFullPath, newFullPath) == Constant.MOVE_FOLDER_SUCCESS) {
			String oldDocumentPathOfSubDocument;
			String newDocumentPathOfSubDocument;
			
			if (pathDTO.getOldDocumentPath().isEmpty()) {
				oldDocumentPathOfSubDocument = pathDTO.getOldDocumentName();
			} else {
				oldDocumentPathOfSubDocument = pathDTO.getOldDocumentPath() + Constant.SUFFIX + pathDTO.getOldDocumentName();
			}
			
			if (pathDTO.getNewDocumentPath().isEmpty()) {
				newDocumentPathOfSubDocument = pathDTO.getNewDocumentName();
			} else {
				newDocumentPathOfSubDocument = pathDTO.getNewDocumentPath() + Constant.SUFFIX + pathDTO.getNewDocumentName();
			}
			
			List<FolderDO> folderDOs = folderDAO.findAllFolderByUserId(userDO);
			for (int i = 0; i < folderDOs.size(); i++) {
				List<FileDO> fileDOs = new ArrayList<FileDO>(folderDOs.get(i).getFileDO());
				for (int j = 0; j < fileDOs.size(); j++) {
					fileDOForCheckPath = fileDOs.get(j);
					if (StringUtils.startsWith(fileDOForCheckPath.getFilePath(), oldDocumentPathOfSubDocument)) {
						newFilePath = StringUtils.replaceOnce(fileDOForCheckPath.getFilePath(), oldDocumentPathOfSubDocument, newDocumentPathOfSubDocument);
						fileDOForCheckPath.setFilePath(newFilePath);
						fileDAO.updateFile(fileDOForCheckPath);
					}
				}
				folderDOForCheckPath = folderDOs.get(i);
				if (StringUtils.startsWith(folderDOForCheckPath.getFolderPath(), oldDocumentPathOfSubDocument)) {
					newFolderPathForUpdate = StringUtils.replaceOnce(folderDOForCheckPath.getFolderPath(), oldDocumentPathOfSubDocument, newDocumentPathOfSubDocument);
					folderDOForCheckPath.setFolderPath(newFolderPathForUpdate);
					folderDAO.updateFolder(folderDOForCheckPath);
				}
			}
			
			oldFolderDO.setFolderPath(pathDTO.getNewDocumentPath());
			oldFolderDO.setFolderIdParent(newFolderDO.getFolderId());
			folderDAO.updateFolder(oldFolderDO);
			shareFolderService.sendNotificationSynchoronize(pathDTO.getUserEmail());
			return Constant.MOVE_FOLDER_TO_ANOTHER_FOLDER_SUCCESS;
		}
		
		return Constant.MOVE_FOLDER_TO_ANOTHER_FOLDER_FAILURE;
	}

	private String makeNewFolderNameIfExist(PathDTO pathDTO, FolderDO newFolderDO) {
		List<FolderDO> folderDOs = folderDAO.findAllFolderByFolderNamePatternInFolder(pathDTO.getNewDocumentName() + " (%", newFolderDO);
		List<Integer> fileIndexes = new ArrayList<Integer>();
		if (folderDOs == null || folderDOs.isEmpty()) {
			return pathDTO.getNewDocumentName() + " (1)";
		} else {
			for (int i = 0; i < folderDOs.size(); i++) {
				String folderName = folderDOs.get(i).getFolderName();
				if (Character.isDigit(folderName.charAt(folderName.length() - 2))) {
					fileIndexes.add(Character.getNumericValue(folderName.charAt(folderName.length() - 2)));
				}
			}
			int finalIndex = Collections.max(fileIndexes) + 1;
			return pathDTO.getNewDocumentName() + " (" + finalIndex + ")";
		}
	}
	
	private String moveFolderS3(String oldFullPath, String newFullPath) {
		String message = Constant.MOVE_FOLDER_SUCCESS;
		AWSCredentials myCredentials = new BasicAWSCredentials(amazonAccessKey, amazonSecretKey);
		AmazonS3 client = new AmazonS3Client(myCredentials);
		ObjectListing listing = client.listObjects(amazonBucket, oldFullPath);
		List<S3ObjectSummary> summaries = listing.getObjectSummaries();
		while (listing.isTruncated()) {
		   listing = client.listNextBatchOfObjects(listing);
		   summaries.addAll(listing.getObjectSummaries());
		}
		
		String newKey;
		String oldKey;
		String documentName;
		for (S3ObjectSummary s3ObjectSummary : summaries) {
			try {
				oldKey = s3ObjectSummary.getKey();
				if (oldKey != null && oldKey != "") {
					String s3key = s3ObjectSummary.getKey();
					if (!s3key.endsWith(Constant.SUFFIX)) {
						documentName = StringUtils.substringAfterLast(s3key, Constant.SUFFIX);
					} else {
						s3key = StringUtils.substring(s3key, 0, s3key.length() - 1);
						documentName = StringUtils.substringAfterLast(s3key, Constant.SUFFIX) + Constant.SUFFIX;
					}
					newKey = newFullPath + documentName;
				} else {
					newKey = oldKey;
				}
				CopyObjectRequest copyObjectRequest = new CopyObjectRequest(amazonBucket, oldKey, amazonBucket, newKey);
				client.copyObject(copyObjectRequest);
				client.deleteObject(new DeleteObjectRequest(amazonBucket, oldKey));
			} catch (AmazonServiceException ase) {
				logger.error(ase.getMessage());
				message = Constant.MOVE_FOLDER_FAILURE;
			} catch (AmazonClientException ace) {
				logger.error(ace.getMessage());
				message = Constant.MOVE_FOLDER_FAILURE;
			} 
		}
		documentName = StringUtils.substring(oldFullPath, 0, oldFullPath.length() - 1);
		documentName = StringUtils.substringAfterLast(documentName, Constant.SUFFIX) + Constant.SUFFIX;
		client.deleteObject(new DeleteObjectRequest(amazonBucket, newFullPath + documentName));
		return message;
	}
	
	private boolean checkFolderIsExist(String folderName, FolderDO folderDO) {
		List<FolderDO> folderDOs = folderDAO.findAllFolderInFolder(folderDO);
		for (int i = 0; i < folderDOs.size(); i++) {
			if (folderName.equals(folderDOs.get(i).getFolderName())) {
				return true;
			}
		}
		return false;
	}

	@Override
	public FolderInformation getFolderInfomation(String userEmail, String folderPath) {
		FolderInformation folderInfo = new FolderInformation();
		UserDO userDO = userDAO.findUserByUserEmail(userEmail);
		if(userDO != null && !folderPath.isEmpty()){
			String[] folderNameAndFolderPath = splitFolderNameParentAndFolderPathParentInFolderPath(folderPath);
			FolderDO folderDO = folderDAO.findFolderByFolderNameAndFolderPathAndUserId(folderNameAndFolderPath[0], folderNameAndFolderPath[1], userDO);
			if (folderDO == null){
				folderDO = getRealFolderDOFromShareFolder(folderPath, userEmail);
			}
			if (folderDO != null){
				folderInfo.setRealNameOfFolder(folderDO.getFolderName());
				folderInfo.setRealPathToFolder(folderDO.getFolderPath());
				folderInfo.setUserEmailOwner(folderDO.getUserDO().getUserEmail());
			}
		}
		return folderInfo;
	}
	
	@Override
	public Folder getFolderInformationById(Long folderId) {
		FolderDO folderDO = folderDAO.findFolderById(folderId);
		Folder folder = new Folder();
		folder.setFolderName(folderDO.getFolderName());
		folder.setFolderPath(folderDO.getFolderPath());
		return folder; 
	}
	
	@Override
	public String getFolderUrlByFolderId(String folderId) {
		// TODO Auto-generated method stub
		return folderDAO.findFolderById(Long.valueOf(folderId)).getFolderUrl();
	}

	@Override
	public Folder downloadFolder(String folderUrl) {
		Folder folderDownload = new Folder();
		FolderDO folderDO;
		folderDO = folderDAO.findFolderByFolderUrl(folderUrl);
		if (folderDO != null){
			//if (folderDO.getFolderPermission() == Constant.DOCUMENT_PUBLIC){
				folderDownload.setFolderName(folderDO.getFolderName());
				folderDownload.setFolderPath(folderDO.getFolderPath());
				folderDownload.setUserEmail(folderDO.getUserDO().getUserEmail());
				folderDownload.setFolderId(folderDO.getFolderId());
			//}
		}
		return folderDownload;
	}
}
